# coding:utf-8
import json

from pow import ProofOfWork
from block_header import BlockHeader
from transaction import Transaction
from merkle import MerkleTree
from errors import NonceNotFoundError, TransactionVerifyError


# Block class 块类
# by liang fei 2021.06.29


# class: Block
# the block = block header + transaction + others
# Block_header Attributes:
#     timestamp (str): 时间戳 创建块头对象时的时间
#     prev_block_hash (str): 上一块的hash
#     block_hash (str): 当前块的hash
#     hash_merkle_root (str): Hash of the merkle_root
#     reputation_threshold (int): 信誉值 阈值
#     reputation (int): 当前块的信誉值
#     height (int): 块高度
# Block Attributes:
#     _magic_no:
#     _block_header:
#     _transaction:
class Block(object):
    """A Block
    Attributes:
        _magic_no (int): Magic number
        _block_header (Block): Header of the previous Block.
        _transaction (Transaction): transactions of the current Block.
    """
    MAGIC_NO = 0xBCBCBCBC

    # 一个block里只包含一个tx

    def __init__(self, block_header, transaction):
        self._magic_no = self.MAGIC_NO
        self._block_header = block_header
        self._transaction = transaction

        data = [json.dumps(transaction.serialize())]
        # data = [str(transaction.serialize())]

        # print('FLAG1:', data)

        merkle_tree = MerkleTree(data)
        self.set_hash_merkle_root_hash(merkle_tree.root_hash)

        # print('FLAG2:', self.block_header.hash_merkle_root)

    # 工作量证明算法
    def mine(self, bc):
        _pow = ProofOfWork(self)
        nonce = 0
        if not bc.verify_transaction(self._transaction):
            raise TransactionVerifyError('transaction verify error')
        try:
            nonce, _ = _pow.run()
        except NonceNotFoundError as e:
            print(e)
        self._block_header.nonce = nonce

    def validate(self, bc):
        _pow = ProofOfWork(self)
        if not bc.verify_transaction(self._transaction):
            raise TransactionVerifyError('transaction verify error')
        return _pow.validate()
    
    @classmethod
    def new_genesis_block(cls, coin_base_tx):
        block_header = BlockHeader.new_genesis_block_header()
        return cls(block_header, coin_base_tx)

    @property
    def block_header(self):
        return self._block_header

    def set_transactions(self, tx):
        self._transaction = tx

    @property
    def transactions(self):
        return self._transaction

    def set_header_hash(self):
        self._block_header.set_hash()

    def set_hash_merkle_root_hash(self, merkle_root_hash):
        self.block_header.hash_merkle_root = merkle_root_hash

    def get_header_hash(self):
        return self._block_header.block_hash

    # 序列化输出
    def serialize(self):
        return {
            "magic_no": self._magic_no,
            "block_header": self._block_header.serialize(),
            "transaction": self._transaction.serialize()
        }

    # 反序列化 dict -> object
    @classmethod
    def deserialize(cls, data):
        block_header_dict = data['block_header']
        block_header = BlockHeader.deserialize(block_header_dict)
        transaction = data["transaction"]
        tx = Transaction.deserialize(transaction)
        return cls(block_header, tx)

    def __eq__(self, other):
        if isinstance(other, Block):
            return self.block_header.block_hash == other.block_header.block_hash
        return False

    def __repr__(self):
        return 'Block(_block_header=%s)' % self._block_header
